﻿using System;
using System.Net;
using Fadd.Messaging;
using Fadd.Net;

namespace Fadd.IO.Consoles
{
    /// <summary>
    /// Remotely controlled console.
    /// </summary>
    /// <remarks>
    /// Used to be able to control an application through a socket.
    /// </remarks>
	public class RemoteConsole : IConsole, IStartable
	{
        private readonly IMessageBroker _publisher;
        private readonly IConsoleChannel _channel;
        private readonly bool _isClient;

        /// <summary>
        /// Initializes a new instance of the <see cref="RemoteConsole"/> class.
        /// </summary>
        /// <param name="publisher">Used to publish received commands.</param>
        /// <param name="channel">Channel used to transport and receive messages to/from remote end point.</param>
        /// <remarks>
        /// Will act as a server (i.e only receive messages)
        /// </remarks>
        public RemoteConsole(IMessageBroker publisher, IConsoleChannel channel)
        {
            _publisher = publisher;
            _channel = channel;
            channel.CommandReceived += OnCommand;
            _channel.OutputReceived += OnOutput;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RemoteConsole"/> class.
        /// </summary>
        /// <param name="publisher">Used to publish received commands.</param>
        /// <param name="remoteHost">End point to connect to.</param>
        /// <remarks>
        /// Will act as a client (i.e. will generate messages)
        /// </remarks>
        public RemoteConsole(IMessageBroker publisher, IPEndPoint remoteHost)
        {
            _publisher = publisher;
            _channel = new BinaryChannel(remoteHost, ConnectionPolicy.Reconnect);
            _channel.CommandReceived += OnCommand;
            _channel.OutputReceived += OnOutput;
            _isClient = true;
        }

        private void OnOutput(object sender, OutputEventArgs e)
        {
            _publisher.Publish(e.Output);
        }

        private void OnCommand(object sender, CommandEventArgs e)
        {
            _publisher.Publish(new Command(e.Command, e.Arguments));
        }

        /// <summary>
        /// Consume a message from the <see cref="IMessageBroker"/>
        /// </summary>
        /// <param name="message">Message to consume</param>
        public void Consume(ConsoleOutput message)
        {
            if (!_isClient)
                return;

            _channel.Send(message);
        }

        /// <summary>
        /// Send a command
        /// </summary>
        /// <param name="command"></param>
        public void Send(Command command)
        {
            _channel.Send(command);
        }

        /// <summary>
        /// Write a line to the console.
        /// </summary>
        /// <param name="color">Console color</param>
        /// <param name="value">Text to write</param>
        public void WriteLine(ConsoleColor color, string value)
        {
            if (!_isClient)
                return;

            _channel.Send(new ConsoleOutput(color, value));
        }

        /// <summary>
        /// Write a line to the console
        /// </summary>
        /// <param name="value">Line to write</param>
        public void WriteLine(string value)
        {
            if (!_isClient)
                return;

            _channel.Send(new ConsoleOutput(value));
        }

        /// <summary>
        /// Start component
        /// </summary>
        public void Start()
        {
            _channel.Start();
        }

        /// <summary>
        /// Stop component
        /// </summary>
        public void Stop()
        {
            _channel.Stop();
        }
	}

}
